package com.gitee.zhangchenyan.takin.auth;

import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceAdapter;
import com.gitee.zhangchenyan.takin.common.utils.ExceptionUtils;

import javax.naming.AuthenticationException;
import java.io.*;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.TreeMap;

/**
 * @Deacription 验签 实现
 * @Author zl
 * @Date 2021/12/5 10:39
 * @Version 1.0
 **/
@ControllerAdvice
@Slf4j
public class AuthCheckSignRequestBodyAdvice extends RequestBodyAdviceAdapter {


    @Override
    public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return Objects.requireNonNull(methodParameter.getMethod()).isAnnotationPresent(AuthCheckSign.class);
    }

    @SneakyThrows
    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        try {
            AuthCheckSign authCheckSign = methodParameter.getMethodAnnotation(AuthCheckSign.class);
            if (authCheckSign != null) {
                AuthCheckSignParameter authCheckSignParameter = SpringUtil.getBean(AuthCheckSignParameter.class);
                String readBody = IOUtils.toString(httpInputMessage.getBody(), StandardCharsets.UTF_8);
                if(!httpInputMessage.getHeaders().containsKey("sign")){
                    throw new Exception("缺少Header参数sign");
                }
                String sign = httpInputMessage.getHeaders().getFirst("sign");
                Object finalNewBody = md5Verify(readBody, sign, authCheckSignParameter.getKey());
                //验签
                return new HttpInputMessage() {
                    @Override
                    public  HttpHeaders getHeaders() {
                        return httpInputMessage.getHeaders();
                    }

                    @Override
                    public  InputStream getBody() throws IOException {
                        return IOUtils.toInputStream(String.valueOf(finalNewBody), StandardCharsets.UTF_8);
                    }
                };
            }
            return httpInputMessage;
        } catch (Exception ex) {
            String outputStr = ExceptionUtils.outputStr(ex, "解密验签");
            throw new AuthenticationException(outputStr);
        }
    }

    @SneakyThrows
    private Object md5Verify(Object dataObject, String sign, String secretKey) {
        JSONObject jsonObject = JSON.parseObject(dataObject.toString(), Feature.OrderedField);
        TreeMap<String, Object> dataTreeMap = new TreeMap<>(jsonObject);
        StringBuilder beforeSignStr = new StringBuilder();
        for (String key : dataTreeMap.keySet()) {
            if (dataTreeMap.get(key) != null && StringUtils.hasLength(dataTreeMap.get(key).toString())) {
                beforeSignStr.append(key).append("=").append(dataTreeMap.get(key).toString()).append("&");
            }
        }
        beforeSignStr.deleteCharAt(beforeSignStr.length() - 1);
        log.info("md签名前串:" + beforeSignStr + secretKey);
        String sign2 = DigestUtils.md5DigestAsHex((beforeSignStr + secretKey).getBytes(StandardCharsets.UTF_8));
        if (!sign.equals(sign2))
            throw new Exception("验签失败服务器生成的串:" + beforeSignStr);
        return dataObject;
    }
}
